package cn.tedu._03vehicle.base.interceptors;

import io.swagger.v3.oas.models.security.SecurityScheme;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;

import java.time.LocalDateTime;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 网站限流拦截器:单个IP限制在每分钟访问网站不能超过3次，超过3次则认为该客户端存在攻击风险
 */
@Slf4j
public class RateLimitInterceptor implements HandlerInterceptor {
    private static final int MAX_COUNT = 3;
    private static final int MAX_WINDOW_SIZE = 60;

    // 记录IP访问次数: {"192.168.1.11": 2, "192.168.1.12": 3, "192.168.1.13": 1}
    private final ConcurrentHashMap<String, Integer> clientCounts = new ConcurrentHashMap<>();
    //存储IP访问时间:{"192.168.1.11": "2025-07-11 00:00:00", "192.168.1.12":"2025-07-10:00:00:00", "192.168.1.13": "2020-01-01 00:00:00"}
    private final ConcurrentHashMap<String, LocalDateTime> lastResetTimes = new ConcurrentHashMap<>();
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.debug("进入网站限流的拦截器...");
        String clientIp = request.getRemoteAddr();
        LocalDateTime lastResetTime = lastResetTimes.get(clientIp);
        /*关于lastResetTime的判断:
        * 1.该IP地址第1次访问：null; --------->  在两个HashMap中存放数据[计数器为0,设置时间];
        * 2.该IP地址1分钟之前访问过: 非空; ---->  在两个HashMap中更新数据[计数器为0,设置时间];
        * 3.该IP地址1分钟内访问过: 非空;   ---->  获取计数,+1,和3比较...;
        * */
        if (lastResetTime == null || LocalDateTime.now().isAfter(lastResetTime.plusSeconds(MAX_WINDOW_SIZE))){
            clientCounts.put(clientIp, 0); //计数器清零;
            lastResetTimes.put(clientIp, LocalDateTime.now()); //更新时间
        }
        //计数器+1
        Integer count = clientCounts.get(clientIp);
        count = count + 1;
        clientCounts.put(clientIp, count);
        //和3比较
        if (count > MAX_COUNT){
            throw new RuntimeException("访问过于频繁，请稍后再试...");
        }
        return true;//放行
    }
}









